﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MJPGStreamer
{
    public class MjpegStreamer : IDisposable
    {
        private readonly HttpListener listener;
        private readonly List<ClientContext> clientCtxs = new List<ClientContext>();
        private readonly AutoResetEvent autoResetEvent = new AutoResetEvent(false);

        /// <summary>
        /// 从 appsettings 中获取要绑定的url
        /// </summary>
        private readonly string serverUrl;
        private bool isServerStoping = false;
        private byte[] currImage = new byte[0];
        private readonly object _locker = new object();

        private const string BOUNDARY = "--BoundaryString";

        private readonly string CONTENT_TYPE = $"multipart/x-mixed-replace; boundary={BOUNDARY}";

        public MjpegStreamer(string streamUrl)
        {
            serverUrl = streamUrl;
            listener = new HttpListener();
            listener.Prefixes.Add(serverUrl);

            Start();

            Task.Run(() =>
            {
                LoopSendToClient();
            });
        }

        #region 对外方法

        /// <summary>
        /// 给 HTTP Streamer Server 推送 Jpeg 格式图片
        /// </summary>
        /// <param name="imageBytes"></param>
        public void PushImage(byte[] imageBytes)
        {
            if (clientCtxs.Count < 1)
            {
                return; // 没有客户端时不接受图片
            }

            Interlocked.Exchange(ref currImage, imageBytes);

            autoResetEvent.Set(); //通知 LoopSendToClient 方法将图片发送给所有客户端
        }

        #endregion


        #region 私有方法

        /// <summary>
        /// 启动 HTTP Streamer Server
        /// </summary>
        private void Start()
        {
            listener.Start();
            Console.WriteLine("Listening for connections on {0}", serverUrl);

            Task.Run(() =>
            {
                ServerListenLoop();
            });
        }

        /// <summary>
        /// 停止 HTTP Streamer Server
        /// </summary>
        private void Stop()
        {
            isServerStoping = true;
            lock (_locker)
            {
                foreach (var ctx in clientCtxs)
                {
                    ctx.Context?.Response.Abort();
                    Console.WriteLine($"关闭客户端：{ctx.Name}");
                }
            }

            listener.Stop();
            listener.Close();
            Console.WriteLine("停止 MjpegStreamer 服务端");
        }

        private void ServerListenLoop()
        {
            while (listener.IsListening)
            {
                HttpListenerContext ctx;
                try
                {
                    ctx = listener.GetContext();
                    var remoteIpPort = ctx.Request.RemoteEndPoint.ToString();

                    clientCtxs.Add(new ClientContext { Name = remoteIpPort, Context = ctx, });
                    Console.WriteLine($"有客户端连接 {remoteIpPort} 客户端个数：{clientCtxs.Count}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"客户端监听异常，退出 ：{ex.Message}");
                    return;
                }

                try
                {
                    // 先发送个请求头 设置响应类型为MJPEG
                    ctx.Response.ContentType = CONTENT_TYPE;

                    // 刷新输出流，确保数据发送到客户端
                    ctx.Response.OutputStream.Flush();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"客户端写入响应类型失败，{ex.Message}");
                }
            }
        }

        private void ProcessRequest(ClientContext client)
        {
            var ctx = client.Context;
            var data = client.Data;
            if (ctx == null || data == null)
                return;

            try
            {
                var header =
                    $"{BOUNDARY}\r\nContent-Type: image/jpeg\r\nContent-Length: {data.Length}\r\n\r\n";
                byte[] headerBytes = Encoding.UTF8.GetBytes(header);

                ctx.Response.OutputStream.Write(headerBytes, 0, headerBytes.Length);
                ctx.Response.OutputStream.Write(data, 0, data.Length);

                // 刷新输出流，确保数据发送到客户端
                ctx.Response.OutputStream.Flush();
            }
            catch
            {
                ctx.Response.Abort();
                lock (_locker)
                {
                    clientCtxs.Remove(client);
                    Console.WriteLine($"移除客户端：{client.Name} 客户端个数：{clientCtxs.Count}");
                }
            }
        }

        private void LoopSendToClient()
        {
            while (!isServerStoping)
            {
                if (clientCtxs.Count < 1)
                {
                    Thread.Sleep(10);
                    continue;
                }

                autoResetEvent.WaitOne(); //收到图片循环发送给所有客户端

                lock (_locker)
                {
                    foreach (var client in clientCtxs)
                    {
                        client.Data = currImage;
                        Task.Run(() =>
                        {
                            ProcessRequest(client);
                        });
                    }
                }
            }
        }

        #endregion


        #region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            Stop();
        }
        #endregion
    }

    internal class ClientContext
    {
        public string Name { get; set; } = "";

        public HttpListenerContext? Context { get; set; }

        public byte[]? Data { get; set; }
    }
}
